#include "buffer.h"
#include "page.h"
#include <iostream>
#include <fstream>
#include <ctime>
#include <iomanip>
#include <algorithm>
#include <cstring>
#include <future>
#include <climits>

using namespace std;

Buffer* Buffer::instance = nullptr;
//日志文件
const string REPLACEMENT_LOG_FILE = "buffer_replacement.log";
const bool ENABLE_REPLACEMENT_LOGGING = true;

//决策阈值：淘汰错误率
static constexpr double BAD_LRU_THRESHOLD = 0.40;
static constexpr double BAD_LFU_THRESHOLD = 0.30;

//单例
Buffer* Buffer::getInstance() {
    if (instance == nullptr) {
        instance = new Buffer;
        instance->startMonitorThread();
    }
    return instance;
}
void Buffer::release() {
    if (instance) {
        instance->stopMonitorThread();
        delete instance;
        instance = nullptr;
    }
}
Buffer::~Buffer() {
    stopMonitorThread();
    string s = "-1";
    BufferPage* bp = head;
    //脏页回写
    while (bp != nullptr) {
        if (bp->page &&
            strcmp(bp->page->getFileid(), s.c_str()) != 0 &&
            strcmp(bp->page->getPageid(), s.c_str()) != 0) {
            safeAsyncWrite(bp->page);
        }
        BufferPage* toDel = bp;
        bp = bp->next;
        delete toDel;
    }
}

//初始化
void Buffer::initialBuffer() {
    string s = "-1";
    BufferPage* cur = nullptr;
    for (int i = 0; i < BUFFER_POOL_CAPACITY; ++i) {
        auto tmp = new BufferPage();
        tmp->page = make_shared<Page>(s, s);
        tmp->next = nullptr;
        if (!head) { head = cur = tmp; }
        else { cur->next = tmp; cur = tmp; }
    }
}


Page* Buffer::getPage(string fileid, string pageid) {
    lock_guard<mutex> lock(bufferMutex);
    totalRequests.fetch_add(1, memory_order_relaxed);
    string s = "-1";
    if (!head) initialBuffer();

    markHotPage(fileid, pageid);

    BufferPage* bp = findBufferPage(fileid, pageid);
    //命中
    if (bp) {
        hitCount.fetch_add(1, memory_order_relaxed);
        bp->accessCount.fetch_add(1, memory_order_relaxed);
        updateLRU(bp);
        if (bp->accessCount > getHotPageThreshold()) promoteToFront(bp);//热页提升
        lfuUpdateFreq(bp);
        //是否预取
        double hitRate = totalRequests > 0 ? (double)hitCount / totalRequests * 100 : 0;
        if (enablePrefetch.load() && hitRate >= prefetchThreshold)
            prefetchNextPageAsync(fileid, pageid);
        return bp->page.get();
    }

    // 未命中
    missCount.fetch_add(1, memory_order_relaxed);

    //选替换页
    BufferPage* victim = nullptr;
    if (curPolicy == ReplacePolicy::LRU) {
        BufferPage* prev = nullptr, * last = head;
        while (last->next) { prev = last; last = last->next; }
        victim = last;
    }
    else {
        victim = lfuPickVictim();
        if (!victim) { // 如果LFU堆为空，回退到LRU策略
            BufferPage* prev = nullptr, * last = head;
            while (last->next) { prev = last; last = last->next; }
            victim = last;
        }
    }

    string oldFile = victim->page ? victim->page->getFileid() : "-1";
    string oldPage = victim->page ? victim->page->getPageid() : "-1";
    string reason = (curPolicy == ReplacePolicy::LRU ? "LRU" : "LFU");

    recordReplace(victim); //记录替换

    //脏页写回
    if (victim->page &&
        strcmp(victim->page->getFileid(), s.c_str()) != 0 &&
        strcmp(victim->page->getPageid(), s.c_str()) != 0) {
        safeAsyncWrite(victim->page);
    }
    //日志
    logPageReplacementAsync(oldFile, oldPage, fileid, pageid, reason);

    auto newPage = make_shared<Page>(fileid, pageid);
    safeAsyncRead(newPage);
    {
        lock_guard<mutex> plock(victim->pageMutex);
        victim->page = newPage;
        victim->accessCount = 1;
    }
    updateLRU(victim);
    lfuUpdateFreq(victim);
    recordReload(fileid, pageid);

    double hitRate = totalRequests > 0 ? (double)hitCount / totalRequests * 100 : 0;
    if (enablePrefetch.load() && hitRate >= prefetchThreshold)
        prefetchNextPageAsync(fileid, pageid);

    return victim->page.get();
}

Page* Buffer::getPageAsync(string fileid, string pageid) {
    return async(launch::async, &Buffer::getPage, this, fileid, pageid).get();
}

//删除
void Buffer::deleteBufferPage(string fileid, string pageid) {
    lock_guard<mutex> lock(bufferMutex);
    string s = "-1";
    BufferPage* prev = nullptr, * bp = head;
    while (bp) {
        if (bp->page &&
            strcmp(bp->page->getFileid(), fileid.c_str()) == 0 &&
            strcmp(bp->page->getPageid(), pageid.c_str()) == 0) break;
        prev = bp; bp = bp->next;
    }
    if (!bp) return;
    if (bp == head) head = bp->next;
    else prev->next = bp->next;

    if (bp->page &&
        strcmp(bp->page->getFileid(), s.c_str()) != 0 &&
        strcmp(bp->page->getPageid(), s.c_str()) != 0) {
        safeAsyncWrite(bp->page);
    }
    delete bp;

    // ��һ����֡
    BufferPage* tmp = new BufferPage();
    tmp->page = make_shared<Page>(s, s);
    tmp->next = head;
    head = tmp;
}
void Buffer::deleteBufferPageAsync(string fileid, string pageid) {
    async(launch::async, &Buffer::deleteBufferPage, this, fileid, pageid);
}

//打印
void Buffer::print() {
    lock_guard<mutex> lock(bufferMutex);
    BufferPage* p = head;
    while (p) {
        if (p->page) {
            cout << p->page->getFileid() << "  " << p->page->getPageid()
                << " (access=" << p->accessCount << ")\n";
        }
        p = p->next;
    }
}
void Buffer::logBufferStats() {
    lock_guard<mutex> lock(bufferMutex);
    int total = totalRequests.load(memory_order_relaxed);
    int hits = hitCount.load(memory_order_relaxed);
    int miss = missCount.load(memory_order_relaxed);
    int repl = replacementCount.load(memory_order_relaxed);
    double hitRate = total > 0 ? (double)hits / total * 100 : 0;

    cout << "===== Cache Statistics Report =====" << endl;
    cout << "Current replacement policy: " << (curPolicy == ReplacePolicy::LRU ? "LRU" : "LFU") << endl;
    cout << "Total requests: " << total << endl;
    cout << "Cache hits: " << hits << endl;
    cout << "Cache misses: " << miss << endl;
    cout << "Page replacements: " << repl << endl;
    cout << "Cache hit rate: " << hitRate << "%" << endl;
    cout << "Prefetch status: " << (enablePrefetch.load() ? "Enabled" : "Disabled") << endl;


}
void Buffer::logBufferStatsAsync() {
    async(launch::async, &Buffer::logBufferStats, this);
}

//日志记录
void Buffer::writeStatsToLogFile(const string& filename) {
    lock_guard<mutex> lock(bufferMutex);
    ofstream log(filename, ios::app);
    if (!log) return;
    time_t now = time(nullptr);
    string dt = ctime(&now); dt.pop_back();
    int total = totalRequests.load(memory_order_relaxed);
    int hits = hitCount.load(memory_order_relaxed);
    int miss = missCount.load(memory_order_relaxed);
    int repl = replacementCount.load(memory_order_relaxed);
    double hitRate = total > 0 ? (double)hits / total * 100 : 0;
    log << "[" << dt << "] Total requests=" << total << " Cache hits=" << hits
        << " Cache misses=" << miss << " Page replacements=" << repl
        << " Cache hit rate=" << fixed << setprecision(2) << hitRate << "%\n";
}
void Buffer::writeStatsToLogFileAsync(const string& filename) {
    async(launch::async, &Buffer::writeStatsToLogFile, this, filename);
}

//异步读写
void Buffer::safeAsyncWrite(shared_ptr<Page> page) {
    if (!page) return;
    async(launch::async, [page]() {
        page->updateBuffer();
        page->writePage();
        });
}
void Buffer::safeAsyncRead(shared_ptr<Page> page) {
    if (!page) return;
    async(launch::async, [page]() {
        page->readPage();
        page->updateMetaData();
        });
}

//LRU
BufferPage* Buffer::findBufferPage(const string& fileid, const string& pageid) {
    BufferPage* p = head;
    while (p) {
        if (p->page &&
            strcmp(p->page->getFileid(), fileid.c_str()) == 0 &&
            strcmp(p->page->getPageid(), pageid.c_str()) == 0) return p;
        p = p->next;
    }
    return nullptr;
}
void Buffer::updateLRU(BufferPage* accessedPage) {
    if (accessedPage == head) return;
    BufferPage* prev = head;
    while (prev && prev->next != accessedPage) prev = prev->next;
    if (prev) {
        prev->next = accessedPage->next;
        accessedPage->next = head;
        head = accessedPage;
    }
}

//冷热页
void Buffer::promoteToFront(BufferPage* hotPage) {
    if (hotPage == head) return;
    BufferPage* prev = head;
    while (prev && prev->next != hotPage) prev = prev->next;
    if (prev) {
        prev->next = hotPage->next;
        hotPage->next = head;
        head = hotPage;
    }
}
BufferPage* Buffer::findColdestPage() {
    BufferPage* coldest = nullptr, * p = head;
    int minAccess = INT_MAX;
    while (p) {
        if (p->page && strcmp(p->page->getFileid(), "-1") != 0 &&
            p->accessCount.load(memory_order_relaxed) < minAccess) {
            minAccess = p->accessCount.load(memory_order_relaxed);
            coldest = p;
        }
        p = p->next;
    }
    return coldest;
}
int Buffer::getHotPageThreshold() const {
    int totalAccess = 0, pageCount = 0;
    BufferPage* p = head;
    while (p) {
        if (p->page && strcmp(p->page->getFileid(), "-1") != 0) {
            totalAccess += p->accessCount.load(memory_order_relaxed);
            ++pageCount;
        }
        p = p->next;
    }
    if (pageCount == 0) return 3;
    int avg = totalAccess / pageCount;
    return max(3, avg * 2);
}
void Buffer::promoteHotPages() {
    int threshold = getHotPageThreshold();
    vector<BufferPage*> hot;
    BufferPage* p = head;
    while (p) {
        if (p->accessCount.load(memory_order_relaxed) > threshold && p != head)
            hot.push_back(p);
        p = p->next;
    }
    sort(hot.begin(), hot.end(),
        [](BufferPage* a, BufferPage* b) {
            return a->accessCount.load() > b->accessCount.load();
        });
    for (BufferPage* hp : hot) promoteToFront(hp);
}
void Buffer::demoteColdPages() {
    BufferPage* coldest = findColdestPage();
    if (!coldest) return;
    int coldThr = coldest->accessCount.load(memory_order_relaxed) + 2;
    vector<BufferPage*> cold;
    BufferPage* p = head;
    while (p) {
        if (p->accessCount.load(memory_order_relaxed) <= coldThr && p != coldest)
            cold.push_back(p);
        p = p->next;
    }
    BufferPage* last = head;
    while (last && last->next) last = last->next;
    for (BufferPage* cp : cold) {
        BufferPage* prev = head;
        while (prev && prev->next != cp) prev = prev->next;
        if (prev) {
            prev->next = cp->next;
            cp->next = nullptr;
            if (last) { last->next = cp; last = cp; }
        }
    }
}

//监控线程
void Buffer::monitorPerformance() {
    while (!stopMonitor.load()) {
        this_thread::sleep_for(chrono::seconds(15));
        lock_guard<mutex> lock(bufferMutex);
        bool hasValid = false;
        BufferPage* p = head;
        while (p) {
            if (p->page && strcmp(p->page->getFileid(), "-1") != 0) { hasValid = true; break; }
            p = p->next;
        }
        if (!hasValid) continue;
        int total = totalRequests.load(memory_order_relaxed);
        int hits = hitCount.load(memory_order_relaxed);
        double hitRate = total > 0 ? (double)hits / total * 100 : 0;
        optimizeByMetrics(hitRate);
        if (total > 10) {
            promoteHotPages();
            demoteColdPages();
            BufferPage* coldest = findColdestPage();
            if (coldest)
                cout << "Coldest: " << coldest->page->getFileid() << ":"
                << coldest->page->getPageid()
                << " (access=" << coldest->accessCount << ")\n";
        }
    }
}
void Buffer::startMonitorThread() {
    stopMonitor.store(false);
    monitorThread = thread(&Buffer::monitorPerformance, this);
}
void Buffer::stopMonitorThread() {
    stopMonitor.store(true);
    if (monitorThread.joinable()) monitorThread.join();
}
void Buffer::optimizeByMetrics(double hitRate) {
    if (hitRate < 50.0) {
        enablePrefetch.store(false);
        cout << "Performance optimization: Hit rate is low (" << hitRate << "%), disabling prefetching" << endl;

    }
    else if (hitRate > 70.0) {
        enablePrefetch.store(true);
        cout << "Performance optimization: Hit rate is high (" << hitRate << "%), enabling prefetching" << endl;
    }
    // ��̬�����ȵ�ҳ��ֵ
    int hotThreshold = getHotPageThreshold();
    cout << "Hot page threshold adjusted to: " << hotThreshold << " accesses" << endl;
}

//预取
void Buffer::markHotPage(const string& fileid, const string& pageid) {
    string key = fileid + ":" + pageid;
    pageAccessCount[key].fetch_add(1, memory_order_relaxed);
}
void Buffer::prefetchNextPage(const string& fileid, const string& pageid) {
    try {
        int fileAccess = 0;
        string prefix = fileid + ":";
        for (auto& e : pageAccessCount)
            if (e.first.rfind(prefix, 0) == 0)
                fileAccess += e.second.load(memory_order_relaxed);
        if (fileAccess <= getHotPageThreshold() * 2) return;
        int cur = stoi(pageid);
        string nextPage = to_string(cur + 1);
        if (findBufferPage(fileid, nextPage)) return;
        auto prefetched = make_shared<Page>(fileid, nextPage);
        safeAsyncRead(prefetched);
        BufferPage* victim = findColdestPage();
        if (victim && victim != head) {
            string s = "-1";
            string oldFile = victim->page ? victim->page->getFileid() : "-1";
            string oldPage = victim->page ? victim->page->getPageid() : "-1";
            if (victim->page &&
                strcmp(victim->page->getFileid(), s.c_str()) != 0 &&
                strcmp(victim->page->getPageid(), s.c_str()) != 0) {
                safeAsyncWrite(victim->page);
            }
            logPageReplacementAsync(oldFile, oldPage, fileid, nextPage, "PREFETCH");
            {
                lock_guard<mutex> plock(victim->pageMutex);
                victim->page = prefetched;
                victim->accessCount = 1;
            }
            updateLRU(victim);
            lfuUpdateFreq(victim);
        }
    }
    catch (...) {}
}
void Buffer::prefetchNextPageAsync(const string& fileid, const string& pageid) {
    async(launch::async, &Buffer::prefetchNextPage, this, fileid, pageid);
}

//替换日志
void Buffer::logPageReplacement(const string& oldFileId, const string& oldPageId,
    const string& newFileId, const string& newPageId,
    const string& reason) {
    if (!ENABLE_REPLACEMENT_LOGGING) return;
    ofstream log(REPLACEMENT_LOG_FILE, ios::app);
    if (!log) return;
    time_t now = time(nullptr);
    string dt = ctime(&now); dt.pop_back();
    replacementCount.fetch_add(1, memory_order_relaxed);
    int cnt = replacementCount.load(memory_order_relaxed);
    double hitRate = totalRequests > 0 ? (double)hitCount / totalRequests * 100 : 0;
    log << "[" << dt << "] Replacement #" << cnt << "\n";
    log << "  Removed: " << oldFileId << ":" << oldPageId << "\n";
    log << "  Added: " << newFileId << ":" << newPageId << "\n";
    log << "  Reason: " << reason << "\n";
    log << "  Current cache hit rate: " << hitRate << "%\n";
    log << "-------------------------\n";

    log.close();

    cout << "[" << dt << "] Replacement #" << cnt << "\n";
    cout << "  Removed: " << oldFileId << ":" << oldPageId << "\n";
    cout << "  Added: " << newFileId << ":" << newPageId << "\n";
    cout << "  Reason: " << reason << "\n";
    cout << "  Current cache hit rate: " << hitRate << "%\n";
    cout << "-------------------------\n";

}
void Buffer::logPageReplacementAsync(const string& oldFileId, const string& oldPageId,
    const string& newFileId, const string& newPageId,
    const string& reason) {
    if (!ENABLE_REPLACEMENT_LOGGING) return;
    async(launch::async, &Buffer::logPageReplacement, this,
        oldFileId, oldPageId, newFileId, newPageId, reason);
}

//策略切换
void Buffer::recordReplace(BufferPage* victim) {
    int cnt = recentReplaceCnt.fetch_add(1, memory_order_relaxed) + 1;
    if (cnt > STAT_WINDOW) {
        recentReplaceCnt.store(STAT_WINDOW, memory_order_relaxed);
        reloadInWindow.store(0, memory_order_relaxed);
    }
    if (curPolicy == ReplacePolicy::LFU && !lfuHeap.empty()) {
        auto it = bp2idx.find(victim);
        if (it != bp2idx.end()) {
            size_t pos = it->second;
            lfuHeap[pos] = lfuHeap.back();
            bp2idx[lfuHeap[pos].bp] = pos;
            lfuHeap.pop_back();
            bp2idx.erase(it);
            size_t child = pos << 1;
            while (child < lfuHeap.size()) {
                if (child + 1 < lfuHeap.size() &&
                    lfuHeap[child + 1].freq < lfuHeap[child].freq) ++child;
                if (lfuHeap[pos].freq <= lfuHeap[child].freq) break;
                std::swap(lfuHeap[pos], lfuHeap[child]);
                bp2idx[lfuHeap[pos].bp] = pos;
                bp2idx[lfuHeap[child].bp] = child;
                pos = child; child = pos << 1;
            }
        }
    }
}
void Buffer::recordReload(const string& fileid, const string& pageid) {
    if (recentReplaceCnt.load(memory_order_relaxed) > 0)
        reloadInWindow.fetch_add(1, memory_order_relaxed);
    trySwitchPolicy();
}
void Buffer::trySwitchPolicy() {
    int repl = recentReplaceCnt.load(memory_order_relaxed);
    int rel = reloadInWindow.load(memory_order_relaxed);
    if (repl < STAT_WINDOW / 2) return;
    double ratio = static_cast<double>(rel) / repl;
    if (curPolicy == ReplacePolicy::LRU && ratio > BAD_LRU_THRESHOLD) {
        switchToLFU(); return;
    }
    if (curPolicy == ReplacePolicy::LFU && ratio > BAD_LFU_THRESHOLD) {
        switchToLRU(); return;
    }
}
void Buffer::switchToLRU() {
    curPolicy = ReplacePolicy::LRU;
    lfuHeap.clear(); bp2idx.clear();
    cout << "[Policy] Switch to LRU (reloadRatio="
        << static_cast<double>(reloadInWindow.load()) / recentReplaceCnt.load() << ")\n";
}
void Buffer::switchToLFU() {
    curPolicy = ReplacePolicy::LFU;
    lfuBuildHeap();
    cout << "[Policy] Switch to LFU (reloadRatio="
        << static_cast<double>(reloadInWindow.load()) / recentReplaceCnt.load() << ")\n";
}
void Buffer::lfuBuildHeap() {
    lfuHeap.clear(); bp2idx.clear();
    lfuHeap.emplace_back(); // 0 ռλ
    BufferPage* p = head;
    while (p) {
        if (p->page && strcmp(p->page->getFileid(), "-1") != 0) {
            lfuHeap.push_back({ p, p->accessCount.load(memory_order_relaxed) });
            bp2idx[p] = lfuHeap.size() - 1;
        }
        p = p->next;
    }
    for (size_t i = lfuHeap.size() / 2; i > 0; --i) {
        size_t pos = i, child = i << 1;
        while (child < lfuHeap.size()) {
            if (child + 1 < lfuHeap.size() &&
                lfuHeap[child + 1].freq < lfuHeap[child].freq) ++child;
            if (lfuHeap[pos].freq <= lfuHeap[child].freq) break;
            std::swap(lfuHeap[pos], lfuHeap[child]);
            bp2idx[lfuHeap[pos].bp] = pos;
            bp2idx[lfuHeap[child].bp] = child;
            pos = child; child = pos << 1;
        }
    }
}
void Buffer::lfuUpdateFreq(BufferPage* bp) {
    if (curPolicy != ReplacePolicy::LFU) return;
    auto it = bp2idx.find(bp);
    if (it == bp2idx.end()) return;
    size_t pos = it->second;
    lfuHeap[pos].freq += 1;
    while (pos > 1) {
        size_t parent = pos >> 1;
        if (lfuHeap[parent].freq <= lfuHeap[pos].freq) break;
        std::swap(lfuHeap[parent], lfuHeap[pos]);
        bp2idx[lfuHeap[parent].bp] = parent;
        bp2idx[lfuHeap[pos].bp] = pos;
        pos = parent;
    }
}
BufferPage* Buffer::lfuPickVictim() {
    if (lfuHeap.size() <= 1) return nullptr;
    BufferPage* victim = lfuHeap[1].bp;
    lfuHeap[1] = lfuHeap.back();
    bp2idx[lfuHeap[1].bp] = 1;
    lfuHeap.pop_back();
    bp2idx.erase(victim);
    size_t pos = 1, child = 2;
    while (child < lfuHeap.size()) {
        if (child + 1 < lfuHeap.size() &&
            lfuHeap[child + 1].freq < lfuHeap[child].freq) ++child;
        if (lfuHeap[pos].freq <= lfuHeap[child].freq) break;
        std::swap(lfuHeap[pos], lfuHeap[child]);
        bp2idx[lfuHeap[pos].bp] = pos;
        bp2idx[lfuHeap[child].bp] = child;
        pos = child; child = pos << 1;
    }
    return victim;
}